﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;

namespace XGIS
{
    public class XMyFile
    {
        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        struct MyFileHeader
        {
            public double MinX, MinY, MaxX, MaxY;
            public int FeatureCount, ShapeType, FieldCount;
        };

        public void WriteFile(XLayer layer, string filename)
        {
            FileStream fsr = new FileStream(filename, FileMode.Create);
            BinaryWriter bw = new BinaryWriter(fsr);
            //写文件头
            WriteFileHeader(layer, bw);
            //写图层名称
            XTools.WriteString(layer.Name, bw);
            //写属性字段结构
            foreach (XField field in layer.Fields)
            {
                XTools.WriteString(field.datatype.ToString(), bw);
                XTools.WriteString(field.name, bw);
            }
            //写空间对象类型
            foreach (XFeature f in layer.Features)
                WriteFeature(f, bw);
            //其它内容
            bw.Close();
            fsr.Close();
        }

        void WriteFileHeader(XLayer layer, BinaryWriter bw)
        {
            MyFileHeader mfh = new MyFileHeader();
            mfh.MinX = layer.Extent.GetMinX();
            mfh.MinY = layer.Extent.GetMinY();
            mfh.MaxX = layer.Extent.GetMaxX();
            mfh.MaxY = layer.Extent.GetMaxY();
            mfh.FeatureCount = layer.FeatureCount();
            mfh.ShapeType = (int)(layer.ShapeType);
            mfh.FieldCount = layer.Fields.Count;
            bw.Write(XTools.ToBytes(mfh));
        }


        void WriteFeature(XFeature feature, BinaryWriter bw)
        {
            if (feature.Spatial is XPointSpatial)
            {
                ((XPointSpatial)feature.Spatial).Centroid.WriteVertex(bw);
            }
            if (feature.Spatial is XLineSpatial)
            {
                WriteMultipleVertexes(((XLineSpatial)feature.Spatial).AllVertexes, bw);
            }
            if (feature.Spatial is XPolygonSpatial)
            {
                WriteMultipleVertexes(((XPolygonSpatial)feature.Spatial).AllVertexes, bw);
            }
            WriteAttribute(feature.Attribute, bw);
        }
        void WriteMultipleVertexes(List<XVertex> _Vertexes, BinaryWriter _bw)
        {
            _bw.Write(_Vertexes.Count);
            for (int vc = 0; vc < _Vertexes.Count; vc++)
                _Vertexes[vc].WriteVertex(_bw);
        }
        void WriteAttribute(XAttribute _Attribute, BinaryWriter _bw)
        {
            _bw.Write(_Attribute.Values.Count);
            foreach (object value in _Attribute.Values)
                XTools.WriteString(value.ToString(), _bw);
        }
        public XLayer ReadFile(string filename)
        {
            FileStream fsr = new FileStream(filename, FileMode.Open);
            BinaryReader br = new BinaryReader(fsr);
            //读文件头
            MyFileHeader mfh = (MyFileHeader)XTools.FromBytes(br, typeof(MyFileHeader));
            //读图层名称
            string name = XTools.ReadString(br);
            //读属性字段结构
            List<XField> fields = ReadFields(mfh.FieldCount, br);
            //定义图层
            SHAPETYPE ShapeType = (SHAPETYPE)Enum.Parse(typeof(SHAPETYPE), mfh.ShapeType.ToString());
            XExtent extent = new XExtent(new XVertex(mfh.MinX, mfh.MinY), new XVertex(mfh.MaxX, mfh.MaxY));
            XLayer layer = new XLayer(name, ShapeType, extent, fields);
            //读空间对象类型
            for (int i = 0; i < mfh.FeatureCount; i++)
            {
                XSpatial spatial = ReadSpatial(ShapeType, br);
                XAttribute attribute = ReadAttribute(br, fields);
                layer.AddFeature(new XFeature(spatial, attribute));
            }
            //关闭文件并返回结果
            br.Close();
            fsr.Close();
            return layer;
        }

        private XAttribute ReadAttribute(BinaryReader br, List<XField> fields)
        {
            XAttribute a = new XAttribute();
            int count = br.ReadInt32();
            for (int vc = 0; vc < count; vc++)
                a.AddValue(fields[vc].MyFileValueToObject(XTools.ReadString(br)));
            return a;
        }

        List<XField> ReadFields(int FieldCount, BinaryReader br)
        {
            List<XField> fields = new List<XField>();
            for (int fieldindex = 0; fieldindex < FieldCount; fieldindex++)
            {
                Type fieldtype = Type.GetType(XTools.ReadString(br));
                string fieldname = XTools.ReadString(br);
                fields.Add(new XField(fieldtype, fieldname));
            }
            return fields;
        }

        XSpatial ReadSpatial(SHAPETYPE shapeType, BinaryReader br)
        {
            if (shapeType == SHAPETYPE.point)
                return new XPointSpatial(new XVertex(br));
            if (shapeType == SHAPETYPE.line)
                return new XLineSpatial(ReadMultipleVertexes(br));
            if (shapeType == SHAPETYPE.polygon)
                return new XPolygonSpatial(ReadMultipleVertexes(br));
            return null;
        }

        List<XVertex> ReadMultipleVertexes(BinaryReader br)
        {
            List<XVertex> vs = new List<XVertex>();
            int vcount = br.ReadInt32();
            for (int vc = 0; vc < vcount; vc++)
                vs.Add(new XVertex(br));
            return vs;
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct DBFField
    {
        public byte b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11;
        public char FieldType;
        public int DisplacementInRecord;
        public byte LengthOfField;
        public byte NumberOfDecimalPlaces;
        public byte Unused19, Unused20, Unused21, Unused22,
                     Unused23, Unused24, Unused25, Unused26, Unused27,
                     Unused28, Unused29, Unused30, Unused31, Unused32;
    }
    public class XField
    {
        public Type datatype;
        public string name;

        DBFField dbfField;
        public XField(BinaryReader br)
        {
            dbfField = (DBFField)XTools.FromBytes(br, typeof(DBFField));
            byte[] bs = new byte[] {dbfField.b1,dbfField.b2,dbfField.b3, dbfField.b4,dbfField.b5,
                dbfField.b6,dbfField.b7,dbfField.b8,dbfField.b9,dbfField.b10,dbfField.b11};
            name = XTools.BytesToString(bs).Trim();
            switch (dbfField.FieldType)
            {
                case 'C':  //字符型  允许输入各种字符
                    datatype = Type.GetType("System.String");
                    break;
                case 'D':  //日期型  用于区分年、月、日的数字和一个字符，内部存储按照YYYYMMDD格式。
                    datatype = Type.GetType("System.String");
                    break;
                case 'N':  //数值型
                    if (dbfField.NumberOfDecimalPlaces == 0)
                        datatype = Type.GetType("System.Int32");
                    else
                        datatype = Type.GetType("System.Double");
                    break;
                case 'F':
                    datatype = Type.GetType("System.Double");
                    break;
                case 'B':  //二进制 允许输入各种字符
                    datatype = Type.GetType("System.String");
                    break;
                case 'G':  //General or OLE                            
                    datatype = Type.GetType("System.String");
                    break;
                case 'L':  //逻辑型，表示没有初始化
                    datatype = Type.GetType("System.String");
                    break;
                case 'M': //Memo
                    datatype = Type.GetType("System.String");
                    break;
                default:
                    break;
            }
        }
        public XField(Type _datatype, string _name)
        {
            datatype = _datatype;
            name = _name;
        }

        internal object DBFValueToObject(BinaryReader br)
        {
            byte[] temp = br.ReadBytes(dbfField.LengthOfField);
            string sv = XTools.BytesToString(temp).Trim();
            if (datatype == Type.GetType("System.String"))
                return sv;
            else if (datatype == Type.GetType("System.Double"))
                return double.Parse(sv);
            else if (datatype == Type.GetType("System.Int32"))
                return int.Parse(sv);
            return sv;
        }

        internal object MyFileValueToObject(string value)
        {
            if (datatype == Type.GetType("System.String"))
                return value;
            else if (datatype == Type.GetType("System.Double"))
                return double.Parse(value);
            else if (datatype == Type.GetType("System.Int32"))
                return int.Parse(value);
            //可持续写下去，处理所有可能的数据类型，比如Boolean，Int64等
            return value;
        }
    }




    public class XTools
    {
        public static void WriteString(string s, BinaryWriter bw)
        {
            bw.Write(s.Length);
            byte[] sbytes = Encoding.GetEncoding("gb2312").GetBytes(s);
            bw.Write(sbytes);
        }



        public static string BytesToString(byte[] byteArray)
        {
            int count = byteArray.Length;
            for (int i = 0; i < byteArray.Length; i++)
            {
                if (byteArray[i] == 0)
                {
                    count = i;
                    break;
                }
            }
            return Encoding.GetEncoding("gb2312").GetString(byteArray, 0, count);
        }

        public static Object FromBytes(BinaryReader br, Type type)
        {
            byte[] buff = br.ReadBytes(Marshal.SizeOf(type));
            GCHandle handle = GCHandle.Alloc(buff, GCHandleType.Pinned);
            Object result = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), type);
            handle.Free();
            return result;
        }

        public static byte[] ToBytes(object c)
        {
            byte[] bytes = new byte[Marshal.SizeOf(c.GetType())];
            GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
            Marshal.StructureToPtr(c, handle.AddrOfPinnedObject(), false);
            handle.Free();
            return bytes;
        }

        public static Point[] GetScreenPoints(List<XVertex> _Vertexes, XView _View)
        {
            Point[] points = new Point[_Vertexes.Count];
            for (int i = 0; i < points.Length; i++)
            {
                points[i] = _View.ToScreenPoint(_Vertexes[i]);
            }
            return points;
        }

        public static XVertex CalculateCentroid(List<XVertex> _Vertexes)
        {
            if (_Vertexes.Count == 0) return null;
            double x = 0, y = 0;
            for (int i = 0; i < _Vertexes.Count; i++)
            {
                x += _Vertexes[i].X;
                y += _Vertexes[i].Y;
            }
            return new XVertex(x / _Vertexes.Count, y / _Vertexes.Count);
        }
        public static XExtent CalculateExtent(List<XVertex> _Vertexes)
        {
            if (_Vertexes.Count == 0) return null;
            double minx, miny, maxx, maxy;
            minx = miny = Double.MaxValue;
            maxx = maxy = Double.MinValue;
            foreach (XVertex v in _Vertexes)
            {
                minx = Math.Min(v.X, minx);
                miny = Math.Min(v.Y, miny);
                maxx = Math.Max(v.X, maxx);
                maxy = Math.Max(v.Y, maxy);
            }
            return new XExtent(new XVertex(minx, miny), new XVertex(maxx, maxy));
        }
        public static double CalculateLength(List<XVertex> _Vertexes)
        {
            double length = 0;
            for (int i = 0; i < _Vertexes.Count - 1; i++)
                length += _Vertexes[i].Distance(_Vertexes[i + 1]);
            return length;
        }

        public static double CalculateArea(List<XVertex> _Vertexes)
        {
            double area = 0;
            for (int i = 0; i < _Vertexes.Count - 1; i++)
                area += VectorProduct(_Vertexes[i], _Vertexes[i + 1]);
            area += VectorProduct(_Vertexes[_Vertexes.Count - 1], _Vertexes[0]);
            return area / 2;
        }
        public static double VectorProduct(XVertex v1, XVertex v2)
        {
            return v1.X * v2.Y - v1.Y * v2.X;
        }

        public static string ReadString(BinaryReader br)
        {
            int length = br.ReadInt32();
            if (length == 0) return "";
            byte[] sbytes = br.ReadBytes(length);
            return Encoding.GetEncoding("gb2312").GetString(sbytes);
        }
    }

    public class XLayer
    {
        public string Name;
        public XExtent Extent;
        public bool DrawAttributeOrNot;
        public int LabelIndex;
        public SHAPETYPE ShapeType;
        public List<XFeature> Features = new List<XFeature>();

        public List<XField> Fields;

        public XLayer(string _Name, SHAPETYPE _ShapeType, XExtent _Extent, List<XField> _Fields)
        {
            Name = _Name;
            ShapeType = _ShapeType;
            Extent = _Extent;
            Fields = _Fields;
        }
        public void Draw(Graphics _Graphics, XView _View)
        {
            for (int i = 0; i < Features.Count; i++)
            {
                if (Features[i].Spatial.Extent.IntersectWith(_View.CurrentMapExtent))
                    Features[i].Draw(_Graphics, _View, DrawAttributeOrNot, LabelIndex);
            }
        }
        public void AddFeature(XFeature feature)
        {
            Features.Add(feature);
        }
        public int FeatureCount()
        {
            return Features.Count;
        }

        public XFeature GetFeature(int i)
        {
            return Features[i];
        }
    }


    public enum SHAPETYPE
    {
        point = 1,
        line = 3,
        polygon = 5
    };

    public class XShapefile
    {
        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        struct DBFHeader
        {
            public char FileType, Year, Month, Day;
            public Int32 RecordCount;
            public Int16 HeaderLength, RecordLength;
            public long Unused1, Unused2;  //16个reserved
            public char Unused3, Unused4;
            public Int16 Unused5;
        }



        private static void ReadDBFFile(string dbffilename, List<XField> fields, List<XAttribute> attributes)
        {
            FileStream fsr = new FileStream(dbffilename, FileMode.Open);
            BinaryReader br = new BinaryReader(fsr);
            DBFHeader dh = (DBFHeader)XTools.FromBytes(br, typeof(DBFHeader));
            int FieldCount = (dh.HeaderLength - 33) / 32;
            //读字段结构
            fields.Clear();
            for (int i = 0; i < FieldCount; i++)
                fields.Add(new XField(br));
            byte END = br.ReadByte();  //1个字节作为记录项终止标识。
            //读具体数值
            attributes.Clear();
            for (int i = 0; i < dh.RecordCount; i++)
            {
                XAttribute attribute = new XAttribute();
                char tempchar = (char)br.ReadByte();  //每个记录的开始都有一个起始字节
                for (int j = 0; j < FieldCount; j++)
                    attribute.AddValue(fields[j].DBFValueToObject(br));
                attributes.Add(attribute);
            }
            br.Close();
            fsr.Close();
        }


        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        struct ShapefileHeader
        {
            public int Unused1, Unused2, Unused3, Unused4;
            public int Unused5, Unused6, Unused7, Unused8;
            public int ShapeType;
            public double Xmin;
            public double Ymin;
            public double Xmax;
            public double Ymax;
            public double Unused9, Unused10, Unused11, Unused12;
        };

        ShapefileHeader ReadFileHeader(BinaryReader br)
        {
            byte[] buff = br.ReadBytes(Marshal.SizeOf(typeof(ShapefileHeader)));
            GCHandle handle = GCHandle.Alloc(buff, GCHandleType.Pinned);
            ShapefileHeader header = (ShapefileHeader)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(ShapefileHeader));
            handle.Free();
            return header;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        struct RecordHeader
        {
            public int RecordNumber;
            public int RecordLength;
            public int ShapeType;
        };

        RecordHeader ReadRecordHeader(BinaryReader br)
        {
            byte[] buff = br.ReadBytes(Marshal.SizeOf(typeof(RecordHeader)));
            GCHandle handle = GCHandle.Alloc(buff, GCHandleType.Pinned);
            RecordHeader header = (RecordHeader)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(RecordHeader));
            handle.Free();
            return header;
        }

        int FromBigToLittle(int bigvalue)
        {
            byte[] bigbytes = new byte[4];
            GCHandle handle = GCHandle.Alloc(bigbytes, GCHandleType.Pinned);
            Marshal.StructureToPtr(bigvalue, handle.AddrOfPinnedObject(), false);
            handle.Free();
            byte b2 = bigbytes[2];
            byte b3 = bigbytes[3];
            bigbytes[3] = bigbytes[0];
            bigbytes[2] = bigbytes[1];
            bigbytes[1] = b2;
            bigbytes[0] = b3;
            return BitConverter.ToInt32(bigbytes, 0);
        }

        public XLayer ReadShapefile(string shpfilename)
        {
            //打开文件和读取工具
            FileStream fsr = new FileStream(shpfilename, FileMode.Open);
            BinaryReader br = new BinaryReader(fsr);
            //读取文件头
            ShapefileHeader sfh = ReadFileHeader(br);
            //获得空间对象类型
            SHAPETYPE ShapeType = (SHAPETYPE)Enum.Parse(typeof(SHAPETYPE), sfh.ShapeType.ToString());
            //获得空间范围
            XExtent extent = new XExtent(new XVertex(sfh.Xmin, sfh.Ymin),
                new XVertex(sfh.Xmax, sfh.Ymax));
            //读dbf文件
            List<XField> fields = new List<XField>();
            List<XAttribute> attributes = new List<XAttribute>();
            ReadDBFFile(shpfilename.Replace(".shp", ".dbf"), fields, attributes);
            //初始化图层
            XLayer layer = new XLayer(shpfilename, ShapeType, extent, fields);
            int index = 0;
            while (br.PeekChar() != -1)
            {
                //读记录头
                RecordHeader rh = ReadRecordHeader(br);
                int RecordLength = FromBigToLittle(rh.RecordLength) * 2 - 4;
                byte[] RecordContent = br.ReadBytes(RecordLength);
                //开始读实际的空间数据
                if (ShapeType == SHAPETYPE.point)
                {
                    XPointSpatial onepoint = ReadPoint(RecordContent);
                    XFeature onefeature = new XFeature(onepoint, attributes[index]);
                    layer.AddFeature(onefeature);
                    index++;
                }
                else if (ShapeType == SHAPETYPE.line)
                {
                    List<XLineSpatial> lines = ReadLines(RecordContent);
                    for (int i = 0; i < lines.Count; i++)
                    {
                        XFeature onefeature = new XFeature(lines[i], attributes[index]);
                        layer.AddFeature(onefeature);
                    }
                    index++;
                }
                else if (ShapeType == SHAPETYPE.polygon)
                {
                    List<XPolygonSpatial> polygons = ReadPolygons(RecordContent);
                    for (int i = 0; i < polygons.Count; i++)
                    {
                        XFeature onefeature = new XFeature(polygons[i], attributes[index]);
                        layer.AddFeature(onefeature);
                    }
                    index++;
                }
            }
            //关闭读取工具和文件
            br.Close();
            fsr.Close();
            return layer;
        }

        XPointSpatial ReadPoint(byte[] RecordContent)
        {
            double x = BitConverter.ToDouble(RecordContent, 0);
            double y = BitConverter.ToDouble(RecordContent, 8);
            return new XPointSpatial(new XVertex(x, y));
        }

        List<XLineSpatial> ReadLines(byte[] RecordContent)
        {
            int N = BitConverter.ToInt32(RecordContent, 32);
            int M = BitConverter.ToInt32(RecordContent, 36);
            int[] parts = new int[N + 1];
            for (int i = 0; i < N; i++)
                parts[i] = BitConverter.ToInt32(RecordContent, 40 + i * 4);
            parts[N] = M;
            List<XLineSpatial> lines = new List<XLineSpatial>();
            for (int i = 0; i < N; i++)
            {
                List<XVertex> vertexs = new List<XVertex>();
                for (int j = parts[i]; j < parts[i + 1]; j++)
                {
                    double x = BitConverter.ToDouble(RecordContent, 40 + N * 4 + j * 16);
                    double y = BitConverter.ToDouble(RecordContent, 40 + N * 4 + j * 16 + 8);
                    vertexs.Add(new XVertex(x, y));
                }
                lines.Add(new XLineSpatial(vertexs));
            }
            return lines;
        }

        List<XPolygonSpatial> ReadPolygons(byte[] RecordContent)
        {
            int N = BitConverter.ToInt32(RecordContent, 32);
            int M = BitConverter.ToInt32(RecordContent, 36);
            int[] parts = new int[N + 1];
            for (int i = 0; i < N; i++)
                parts[i] = BitConverter.ToInt32(RecordContent, 40 + i * 4);
            parts[N] = M;
            List<XPolygonSpatial> polygons = new List<XPolygonSpatial>();
            for (int i = 0; i < N; i++)
            {
                List<XVertex> vertexs = new List<XVertex>();
                for (int j = parts[i]; j < parts[i + 1]; j++)
                {
                    double x = BitConverter.ToDouble(RecordContent, 40 + N * 4 + j * 16);
                    double y = BitConverter.ToDouble(RecordContent, 40 + N * 4 + j * 16 + 8);
                    vertexs.Add(new XVertex(x, y));
                }
                polygons.Add(new XPolygonSpatial(vertexs));
            }
            return polygons;
        }


    }


    public enum XMapActions
    {
        zoomin, zoomout,
        moveup, movedown, moveleft, moveright
    };

    public class XView
    {
        public XExtent CurrentMapExtent;
        Rectangle MapWindowSize;
        double MapMinX, MapMinY;
        int WinW, WinH;
        double MapW, MapH;
        double ScaleX, ScaleY;

        public XView(XExtent _Extent, Rectangle _Rectangle)
        {
            Update(_Extent, _Rectangle);
        }
        public void UpdateExtent(XMapActions _Action)
        {
            CurrentMapExtent.Change(_Action);
            Update(CurrentMapExtent, MapWindowSize);
        }

        public void UpdateExtent(XExtent _Extent)
        {
            CurrentMapExtent.CopyFrom(_Extent);
            Update(CurrentMapExtent, MapWindowSize);
        }

        public void Update(XExtent _extent, Rectangle _Rectangle)
        {
            CurrentMapExtent = _extent;
            MapWindowSize = _Rectangle;
            WinW = MapWindowSize.Width;
            WinH = MapWindowSize.Height;
            ScaleX = CurrentMapExtent.GetWidth() / WinW;
            ScaleY = CurrentMapExtent.GetHeight() / WinH;
            ScaleX = Math.Max(ScaleX, ScaleY);
            ScaleY = ScaleX;
            MapW = MapWindowSize.Width * ScaleX;
            MapH = MapWindowSize.Height * ScaleY;
            XVertex center = CurrentMapExtent.GetCenter();
            MapMinX = center.X - MapW / 2;
            MapMinY = center.Y - MapH / 2;
            CurrentMapExtent = new XExtent(new XVertex(MapMinX, MapMinY), new XVertex(MapMinX + MapW, MapMinY + MapH));
        }
        public Point ToScreenPoint(XVertex _Location)
        {
            double ScreenX = (_Location.X - MapMinX) / ScaleX;
            double ScreenY = WinH - (_Location.Y - MapMinY) / ScaleY;
            return new Point((int)ScreenX, (int)ScreenY);
        }

        public XVertex ToMapVertex(Point _Point)
        {
            double MapX = ScaleX * _Point.X + MapMinX;
            double MapY = ScaleY * (WinH - _Point.Y) + MapMinY;
            return new XVertex(MapX, MapY);
        }

        internal void UpdateWindowSize(Rectangle clientRectangle)
        {
            Update(CurrentMapExtent, clientRectangle);
        }

        internal void OffsetCenter(XVertex vFrom, XVertex vTo)
        {
            Point pFrom = ToScreenPoint(vFrom);
            Point pTo = ToScreenPoint(vTo);
            Point newCenterPoint = new Point(MapWindowSize.Width / 2 - pTo.X + pFrom.X,
                MapWindowSize.Height / 2 - pTo.Y + pFrom.Y);
            XVertex newCenter = ToMapVertex(newCenterPoint);
            CurrentMapExtent.UpdateCenter(newCenter);
            Update(CurrentMapExtent, MapWindowSize);
        }
    }

    public class XExtent
    {
        public XVertex BottomLeft;
        public XVertex UpRight;

        public XExtent(XVertex _oneCorner, XVertex _anotherCorner)
        {
            UpRight = new XVertex(Math.Max(_anotherCorner.X, _oneCorner.X),
                                Math.Max(_anotherCorner.Y, _oneCorner.Y));
            BottomLeft = new XVertex(Math.Min(_anotherCorner.X, _oneCorner.X),
                                Math.Min(_anotherCorner.Y, _oneCorner.Y));
        }

        public void CopyFrom(XExtent _Extent)
        {
            UpRight.CopyFrom(_Extent.UpRight);
            BottomLeft.CopyFrom(_Extent.BottomLeft);
        }

        public double GetMinX()
        {
            return BottomLeft.X;
        }
        public double GetMaxX()
        {
            return UpRight.X;
        }
        public double GetMinY()
        {
            return BottomLeft.Y;
        }
        public double GetMaxY()
        {
            return UpRight.Y;
        }
        public double GetWidth()
        {
            return UpRight.X - BottomLeft.X;
        }
        public double GetHeight()
        {
            return UpRight.Y - BottomLeft.Y;
        }

        double ZoomingFactor = 2;
        double MovingFactor = 0.25;
        internal void Change(XMapActions action)
        {
            double newminx = BottomLeft.X, newminy = BottomLeft.Y,
                           newmaxx = UpRight.X, newmaxy = UpRight.Y;
            switch (action)
            {
                case XMapActions.zoomin:
                    newminx = ((GetMinX() + GetMaxX()) - GetWidth() / ZoomingFactor) / 2;
                    newminy = ((GetMinY() + GetMaxY()) - GetHeight() / ZoomingFactor) / 2;
                    newmaxx = ((GetMinX() + GetMaxX()) + GetWidth() / ZoomingFactor) / 2;
                    newmaxy = ((GetMinY() + GetMaxY()) + GetHeight() / ZoomingFactor) / 2;
                    break;
                case XMapActions.zoomout:
                    newminx = ((GetMinX() + GetMaxX()) - GetWidth() * ZoomingFactor) / 2;
                    newminy = ((GetMinY() + GetMaxY()) - GetHeight() * ZoomingFactor) / 2;
                    newmaxx = ((GetMinX() + GetMaxX()) + GetWidth() * ZoomingFactor) / 2;
                    newmaxy = ((GetMinY() + GetMaxY()) + GetHeight() * ZoomingFactor) / 2;
                    break;
                case XMapActions.moveup:
                    newminy = GetMinY() - GetHeight() * MovingFactor;
                    newmaxy = GetMaxY() - GetHeight() * MovingFactor;
                    break;
                case XMapActions.movedown:
                    newminy = GetMinY() + GetHeight() * MovingFactor;
                    newmaxy = GetMaxY() + GetHeight() * MovingFactor;
                    break;
                case XMapActions.moveleft:
                    newminx = GetMinX() + GetWidth() * MovingFactor;
                    newmaxx = GetMaxX() + GetWidth() * MovingFactor;
                    break;
                case XMapActions.moveright:
                    newminx = GetMinX() - GetWidth() * MovingFactor;
                    newmaxx = GetMaxX() - GetWidth() * MovingFactor;
                    break;
            }
            UpRight.X = newmaxx;
            UpRight.Y = newmaxy;
            BottomLeft.X = newminx;
            BottomLeft.Y = newminy;
        }

        internal XVertex GetCenter()
        {
            return new XVertex((UpRight.X + BottomLeft.X) / 2, (UpRight.Y + BottomLeft.Y) / 2);
        }

        internal bool IntersectWith(XExtent _Extent)
        {
            return !(GetMaxX() < _Extent.GetMinX() || GetMinX() > _Extent.GetMaxX() ||
                 GetMaxY() < _Extent.GetMinY() || GetMinY() > _Extent.GetMaxY());
        }

        internal void UpdateCenter(XVertex _NewCenter)
        {
            double width = GetWidth();
            double height = GetHeight();
            UpRight = new XVertex(_NewCenter.X + width / 2, _NewCenter.Y + height / 2);
            BottomLeft = new XVertex(_NewCenter.X - width / 2, _NewCenter.Y - height / 2);
        }
    }

    public class XAttribute
    {
        public ArrayList Values = new ArrayList();
        public void AddValue(object _Object)
        {
            Values.Add(_Object);
        }
        public object GetValue(int _Index)
        {
            return Values[_Index];
        }

        public void Draw(Graphics _Graphics, XView _View, XVertex _Location, int _Index)
        {
            Point screenPoint = _View.ToScreenPoint(_Location);
            _Graphics.DrawString(GetValue(_Index).ToString(), new Font("宋体", 20),
                new SolidBrush(Color.Green), screenPoint);
        }
    }

    public abstract class XSpatial
    {
        public XVertex Centroid;
        public XExtent Extent;

        public abstract void Draw(Graphics _Graphics, XView _View);
    }

    public class XFeature
    {
        public XSpatial Spatial;
        public XAttribute Attribute;

        public XFeature(XSpatial _Spatial, XAttribute _Attribute)
        {
            Spatial = _Spatial;
            Attribute = _Attribute;
        }
        public void Draw(Graphics _Graphics, XView _View, bool _DrawAttributeOrNot, int _Index)
        {
            Spatial.Draw(_Graphics, _View);
            if (_DrawAttributeOrNot)
                Attribute.Draw(_Graphics, _View, Spatial.Centroid, _Index);
        }
        public object GetAttribute(int _Index)
        {
            return Attribute.GetValue(_Index);
        }
    }

    public class XVertex
    {
        public double X;
        public double Y;
        private BinaryReader br;

        public XVertex(double _X, double _Y)
        {
            X = _X;
            Y = _Y;
        }

        public XVertex(BinaryReader br)
        {
            X = br.ReadDouble();
            Y = br.ReadDouble();
        }

        public double Distance(XVertex _AnotherVertex)
        {
            return Math.Sqrt((X - _AnotherVertex.X) * (X - _AnotherVertex.X) +
                (Y - _AnotherVertex.Y) * (Y - _AnotherVertex.Y));
        }
        public void CopyFrom(XVertex _V)
        {
            X = _V.X;
            Y = _V.Y;
        }

        public void WriteVertex(BinaryWriter bw)
        {
            bw.Write(X);
            bw.Write(Y);
        }
    }

    public class XPointSpatial : XSpatial
    {
        public XPointSpatial(XVertex _Centroid)
        {
            Centroid = _Centroid;
            Extent = new XExtent(_Centroid, _Centroid);
        }
        public override void Draw(Graphics _Graphics, XView _View)
        {
            Point screenPoint = _View.ToScreenPoint(Centroid);
            _Graphics.FillEllipse(new SolidBrush(Color.Red),
                new Rectangle(screenPoint.X - 3, screenPoint.Y - 3, 6, 6));
        }
        public double Distance(XVertex _AnotherLocation)
        {
            return Centroid.Distance(_AnotherLocation);
        }
    }

    public class XLineSpatial : XSpatial
    {
        public List<XVertex> AllVertexes;
        public double Length;
        public XLineSpatial(List<XVertex> _Vertexes)
        {
            AllVertexes = _Vertexes;
            Centroid = XTools.CalculateCentroid(_Vertexes);
            Extent = XTools.CalculateExtent(_Vertexes);
            Length = XTools.CalculateLength(_Vertexes);
        }
        public override void Draw(Graphics _Graphics, XView _View)
        {
            Point[] points = XTools.GetScreenPoints(AllVertexes, _View);
            _Graphics.DrawLines(new Pen(Color.Red, 2), points);
        }
    }

    public class XPolygonSpatial : XSpatial
    {
        public List<XVertex> AllVertexes;
        public double Area;
        public Color BufferColor; // 添加颜色属性

        // 接受颜色参数的构造函数
        public XPolygonSpatial(List<XVertex> _Vertexes, Color color)
        {
            AllVertexes = _Vertexes;
            Centroid = XTools.CalculateCentroid(_Vertexes);
            Extent = XTools.CalculateExtent(_Vertexes);
            Area = XTools.CalculateArea(_Vertexes);
            BufferColor = color; // 保存颜色
        }

        // 接受顶点列表的构造函数
        public XPolygonSpatial(List<XVertex> _Vertexes)
        {
            AllVertexes = _Vertexes;
            Centroid = XTools.CalculateCentroid(_Vertexes);
            Extent = XTools.CalculateExtent(_Vertexes);
            Area = XTools.CalculateArea(_Vertexes);
            BufferColor = Color.Yellow; // 默认颜色
        }

        public override void Draw(Graphics _Graphics, XView _View)
        {
            Point[] points = XTools.GetScreenPoints(AllVertexes, _View);
            _Graphics.FillPolygon(new SolidBrush(BufferColor), points);
            _Graphics.DrawPolygon(new Pen(Color.White, 2), points);
        }
    }

    public class BufferOperations
        {
        // 创建点缓冲区
        public static XPolygonSpatial CreateBufferForPoint(XPointSpatial point, double radiusInMeters, Color color)
        {
            int numberOfVertices = 360;
            double angleStep = 360.0 / numberOfVertices;
            List<XVertex> vertices = new List<XVertex>();

            for (int i = 0; i < numberOfVertices; i++)
            {
                double angleInRadians = Math.PI / 180 * angleStep * i;
                double x = point.Centroid.X + radiusInMeters * Math.Cos(angleInRadians);
                double y = point.Centroid.Y + radiusInMeters * Math.Sin(angleInRadians);
                vertices.Add(new XVertex(x, y));
            }

            vertices.Add(vertices[0]); // 闭合多边形

            // 创建并返回XPolygonSpatial对象，保存颜色
            return new XPolygonSpatial(vertices, color);
        }

        //创建线缓冲区
        public static XPolygonSpatial CreateBufferForLine(XLineSpatial line, double radiusInMeters, Color color)
        {
            List<XVertex> vertices = new List<XVertex>();
            foreach (XVertex vertex in line.AllVertexes)
            {
                int numberOfVertices = 360;
                double angleStep = 360.0 / numberOfVertices;
                for (int i = 0; i < numberOfVertices; i++)
                {
                    double angleInRadians = Math.PI / 180 * angleStep * i;
                    double x = vertex.X + radiusInMeters * Math.Cos(angleInRadians);
                    double y = vertex.Y + radiusInMeters * Math.Sin(angleInRadians);
                    vertices.Add(new XVertex(x, y));
                }
            }

            List<XVertex> convexHull = ConvexHull(vertices);

            // 创建并返回XPolygonSpatial对象，保存颜色
            return new XPolygonSpatial(convexHull, color);
        }


        private static List<XVertex> ConvexHull(List<XVertex> vertices)
            {
                if (vertices.Count < 3)
                    return vertices;

                List<XVertex> hull = new List<XVertex>();
                int l = 0;
                for (int i = 1; i < vertices.Count; i++)
                {
                    if (vertices[i].Y < vertices[l].Y || (vertices[i].Y == vertices[l].Y && vertices[i].X < vertices[l].X))
                        l = i;
                }

                int p = l, q;
                do
                {
                    hull.Add(vertices[p]);
                    q = (p + 1) % vertices.Count;
                    for (int i = 0; i < vertices.Count; i++)
                    {
                        double dx1 = vertices[i].X - vertices[p].X;
                        double dy1 = vertices[i].Y - vertices[p].Y;
                        double dx2 = vertices[q].X - vertices[p].X;
                        double dy2 = vertices[q].Y - vertices[p].Y;
                        if ((dx1 * dy2 - dy1 * dx2) < 0)
                            q = i;
                    }
                    p = q;
                } while (p != l);

                return hull;
            }


        // 创建面缓冲区
        public static XPolygonSpatial CreateBufferForPolygon(XPolygonSpatial polygon, double radiusInMeters, Color color)
        {
            List<XVertex> vertices = new List<XVertex>();
            foreach (XVertex vertex in polygon.AllVertexes)
            {
                int numberOfVertices = 360;
                double angleStep = 360.0 / numberOfVertices;
                for (int i = 0; i < numberOfVertices; i++)
                {
                    double angleInRadians = Math.PI / 180 * angleStep * i;
                    double x = vertex.X + radiusInMeters * Math.Cos(angleInRadians);
                    double y = vertex.Y + radiusInMeters * Math.Sin(angleInRadians);
                    vertices.Add(new XVertex(x, y));
                }
            }

            List<XVertex> simplifiedVertices = DouglasPeuckerAlgorithm(vertices);
            List<XVertex> convexHull = ConvexHull(simplifiedVertices);

            // 创建并返回XPolygonSpatial对象，保存颜色
            return new XPolygonSpatial(convexHull, color);
        }


            // Douglas-Peucker算法实现
            private static List<XVertex> DouglasPeuckerAlgorithm(List<XVertex> points, double tolerance = 1.0)
            {
                if (points.Count < 3)
                    return points;

                int startIdx = 0;
                int endIdx = points.Count - 1;
                double maxDistance = 0;
                int indexFarthest = 0;

                for (int i = 1; i < points.Count - 1; i++)
                {
                    double distance = DistanceFromPointToLineSegment(points[i], points[startIdx], points[endIdx]);
                    if (distance > maxDistance)
                    {
                        maxDistance = distance;
                        indexFarthest = i;
                    }
                }

                if (maxDistance > tolerance)
                {
                    // Divide the segment into two parts
                    List<XVertex> results1 = DouglasPeuckerAlgorithm(points.GetRange(0, indexFarthest + 1), tolerance);
                    List<XVertex> results2 = DouglasPeuckerAlgorithm(points.GetRange(indexFarthest, points.Count - indexFarthest - 1), tolerance);

                    // Combine the results
                    results1.AddRange(results2);
                    return results1;
                }
                else
                {
                    List<XVertex> simplified = new List<XVertex>();
                    simplified.Add(points[startIdx]);
                    simplified.Add(points[endIdx]);
                    return simplified;
                }
            }

            private static double DistanceFromPointToLineSegment(XVertex p, XVertex p1, XVertex p2)
            {
                double dx = p2.X - p1.X;
                double dy = p2.Y - p1.Y;
                if ((dx == 0) && (dy == 0))
                    return Math.Sqrt((p.X - p1.X) * (p.X - p1.X) + (p.Y - p1.Y) * (p.Y - p1.Y));

                double t = ((p.X - p1.X) * dx + (p.Y - p1.Y) * dy) / (dx * dx + dy * dy);
                if (t < 0)
                {
                    t = 0;
                }
                else if (t > 1)
                {
                    t = 1;
                }
                double x = p1.X + t * dx;
                double y = p1.Y + t * dy;
                double dist = Math.Sqrt((p.X - x) * (p.X - x) + (p.Y - y) * (p.Y - y));
                return dist;
            }

        }


    }


